package com.nervenets.general.web.params;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.nervenets.general.Global;
import com.nervenets.general.annotation.IgnoreSwaggerParameter;
import com.nervenets.general.hibernate.DomainObject;
import com.nervenets.general.service.BaseService;
import com.nervenets.general.utils.ClassUtils;
import com.nervenets.general.utils.SpringContextHolder;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import lombok.experimental.SuperBuilder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.FeatureDescriptor;
import java.lang.reflect.ParameterizedType;
import java.util.stream.Stream;

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class FormParams<T extends DomainObject, Service extends BaseService<T>> implements Params {
    @ApiModelProperty("旧对象")
    @IgnoreSwaggerParameter
    @JsonIgnore
    @JSONField(serialize = false)
    protected T old;
    @ApiModelProperty("新对象")
    @IgnoreSwaggerParameter
    @JsonIgnore
    @JSONField(serialize = false)
    protected T now;
    @ApiModelProperty("实体对象ID")
    private long formId;

    public T submit(Service service, HttpServletRequest request, HttpServletResponse response) {
        final T now = this.validateAndBuild(service, request, response);
        Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        bindNow(entityClass, now);
        return now;
    }

    /**
     * 实现此方法，仅做静态数据格式合法性验证，与数据库相关的验证请移步到ServiceImpl的before或after去实现
     *
     * @return T
     */
    @SneakyThrows
    public T validateAndBuild(Service service, HttpServletRequest request, HttpServletResponse response) {
        ClassUtils.trimAllStringValue(this);
        Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        T t = entityClass.newInstance();
        if (getFormId() > 0) {
            t = service.findOne(getFormId(), "您要编辑的数据不存在，请刷新后再试。");
        }
        bindOld(entityClass, t);

        BeanUtils.copyProperties(this, t, Global.defaultIgnoreProperties(nullProperties()));
        return t;
    }

    @SneakyThrows
    public void bindOld(Class<T> entityClass, T t) {
        T temp = entityClass.newInstance();
        BeanUtils.copyProperties(t, temp);
        this.old = temp;
    }

    @SneakyThrows
    public void bindNow(Class<T> entityClass, T t) {
        T temp = entityClass.newInstance();
        BeanUtils.copyProperties(t, temp);
        this.now = temp;
    }

    /**
     * 获取表单格式的数据
     *
     * @param t 对象
     */
    public void convert(T t) {
        BeanUtils.copyProperties(t, this);
        this.formId = t.getId();
    }

    protected String[] nullProperties() {
        if (!exceptNullProperties()) return null;
        BeanWrapper wrappedSource = new BeanWrapperImpl(this);
        return Stream.of(wrappedSource.getPropertyDescriptors())
                .map(FeatureDescriptor::getName)
                .filter(name -> !"class".equalsIgnoreCase(name))
                .filter(propertyName -> wrappedSource.getPropertyValue(propertyName) == null).toArray(String[]::new);
    }

    public boolean exceptNullProperties() {
        return true;
    }

    protected <S> S getBean(Class<S> requiredType) {
        return SpringContextHolder.getBean(requiredType);
    }
}
